package fudan.se.project.service.advanced.conference.action;

import fudan.se.project.controller.request.PaperSubmitRequest;
import fudan.se.project.controller.request.RebuttalSubmitRequest;
import fudan.se.project.controller.response.BaseResponse;
import fudan.se.project.controller.response.PaperResourceResponse;
import fudan.se.project.controller.response.ReviewInfosGetResponse;
import fudan.se.project.domain.User;
import fudan.se.project.domain.conference.Conference;
import fudan.se.project.domain.conference.Conference.ConferenceStage;
import fudan.se.project.domain.conference.post.Post;
import fudan.se.project.domain.conference.post.Rebuttal;
import fudan.se.project.domain.conference.production.Paper;
import fudan.se.project.domain.conference.production.Paper.PaperState;
import fudan.se.project.repository.projection.PaperProjection;
import fudan.se.project.repository.projection.ReviewInfoProjection;
import fudan.se.project.service.infrastructure.entity.conference.ConferenceService;
import fudan.se.project.service.infrastructure.entity.conference.post.PostService;
import fudan.se.project.service.infrastructure.entity.conference.post.RebuttalService;
import fudan.se.project.service.infrastructure.entity.conference.production.PaperService;
import fudan.se.project.service.infrastructure.entity.conference.production.ReviewInfoService;
import fudan.se.project.service.infrastructure.func.conference.action.author.PaperSubmitService;
import fudan.se.project.service.infrastructure.func.conference.action.author.PaperUpdateService;
import fudan.se.project.util.http.ResponseWrapperUtil;
import fudan.se.project.util.user.UserDetailsUtil;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

import static fudan.se.project.repository.projection.PaperProjection.project;
import static fudan.se.project.repository.projection.PaperProjection.projectRebuttals;
import static org.springframework.http.HttpStatus.NOT_ACCEPTABLE;
import static org.springframework.http.HttpStatus.OK;

@Service
@RequiredArgsConstructor
public class AuthorActionService {

    @NonNull
    private final ConferenceService conferenceService;
    @NonNull
    private final ResponseWrapperUtil responseWrapperUtil;
    @NonNull
    private final UserDetailsUtil userDetailsUtil;
    @NonNull
    private final PaperService paperService;
    @NonNull
    private final RebuttalService rebuttalService;
    @NonNull
    private final PaperSubmitService paperSubmitService;
    @NonNull
    private final PaperUpdateService paperUpdateService;
    @NonNull
    private final ReviewInfoService reviewInfoService;
    @NonNull
    private final PostService postService;
    private static final String ARGUMENTS_ERROR = "arguments error";
    private static final String SUCCESS = "success";


    @PreAuthorize("hasAuthority('USER') and (not hasPermission(null,'conference:chair:'+#request.id))")
    public ResponseEntity<?> submitPaper(PaperSubmitRequest request, MultipartFile file) {
        Conference conference = conferenceService.findById(request.getId());
        BaseResponse baseResponse;
        if (conference == null) {
            baseResponse = new BaseResponse("conference not found");
            return responseWrapperUtil.buildResponseBody(baseResponse, NOT_ACCEPTABLE);
        } else if (!conference.getStage().equals(ConferenceStage.CONTRIBUTION_START)) {
            baseResponse = new BaseResponse(ARGUMENTS_ERROR);
            return responseWrapperUtil.buildResponseBody(baseResponse, NOT_ACCEPTABLE);
        } else {
            if (!paperSubmitService.process(request, file)) {
                return responseWrapperUtil.buildBaseResponseBody(ARGUMENTS_ERROR, NOT_ACCEPTABLE);
            }
            baseResponse = new BaseResponse(SUCCESS);
            return responseWrapperUtil.buildResponseBody(baseResponse, OK);
        }
    }

    @PreAuthorize("hasAuthority('USER') and (not hasPermission(null,'conference:chair:'+#id))")
    public ResponseEntity<?> getPapers(long id) {
        User user = userDetailsUtil.currentUser();
        List<Paper> unRebutted = paperService.findUnrebuttedForAuthor(id, user);
        List<Rebuttal> rebutted = rebuttalService.findRebuttalsForAuthor(id, user);

        List<PaperProjection> projectionList = project(unRebutted);
        projectionList.addAll(projectRebuttals(rebutted));

        PaperResourceResponse paperResourceResponse = new PaperResourceResponse(projectionList);
        paperResourceResponse.setMessage(SUCCESS);
        return responseWrapperUtil.buildResponseBody(paperResourceResponse, OK);

    }

    @PreAuthorize("hasAuthority('USER') and  hasPermission(null,'paper:author:'+#id)")
    public ResponseEntity<?> getReviewInfos(long id) {

        Paper paper = paperService.findById(id);
        User userAuthor = userDetailsUtil.currentUser();
        if (paper.getPaperState().equals(PaperState.COMPLETED)) {
            ReviewInfosGetResponse reviewInfosGetResponse = new ReviewInfosGetResponse(
                    ReviewInfoProjection.project(reviewInfoService.findByAuthorUserAndPaperId(userAuthor, id))
            );
            reviewInfosGetResponse.setMessage(SUCCESS);
            return responseWrapperUtil.buildResponseBody(reviewInfosGetResponse, OK);
        } else {
            return responseWrapperUtil.buildBaseResponseBody(
                    ARGUMENTS_ERROR,
                    NOT_ACCEPTABLE
            );
        }
    }

    @PreAuthorize("hasAuthority('USER') and  hasPermission(null,'paper:author:'+#request.id)")
    public ResponseEntity<?> updatePaper(PaperSubmitRequest request, MultipartFile file) {
        Paper paper = paperService.findById(request.getId());
        BaseResponse baseResponse;
        if (!paper.getConference().getStage().equals(ConferenceStage.CONTRIBUTION_START)) {
            baseResponse = new BaseResponse("failed: unable to edit paper in this stage.");
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);
        } else if (!paperUpdateService.process(request, paper, file)) {
            baseResponse = new BaseResponse(ARGUMENTS_ERROR);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);
        } else {
            baseResponse = new BaseResponse(SUCCESS);
            return responseWrapperUtil.buildResponseBody(baseResponse, OK);
        }
    }

    @PreAuthorize("hasAuthority('USER') and  hasPermission(null,'paper:author:'+#request.id)")
    public ResponseEntity<?> submitRebuttal(RebuttalSubmitRequest request) {
        BaseResponse baseResponse = new BaseResponse();
        long paperId = request.getId();

        if (rebuttalService.findByPaperId(paperId) != null) {
            baseResponse.setMessage("rebuttal already exists");
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.METHOD_NOT_ALLOWED);
        }

        Paper paper = paperService.findById(paperId);

        if (!rebuttalService.isRebuttable(paper.getReviewInfos())) {
            baseResponse.setMessage("method not allowed");
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.METHOD_NOT_ALLOWED);
        }

        Conference conference = paper.getConference();

        if (!conference.getStage().equals(ConferenceStage.RESULT_RELEASE)) {
            baseResponse.setMessage("wrong conference stage");
            return responseWrapperUtil.buildResponseBody(baseResponse, NOT_ACCEPTABLE);
        }

        Post post = new Post(paper, "post:" + paper.getTitleText());
        post = postService.save(post);
        Rebuttal rebuttal = new Rebuttal(post, request.getRebuttal());
        rebuttalService.save(rebuttal);
        baseResponse.setMessage(SUCCESS);
        return responseWrapperUtil.buildResponseBody(baseResponse, OK);

    }
}
